$NOMOD51 ; Ax51宏汇编器控制命令：禁止预定义的8051
;------------------------------------------------------------------------------
; File Encoding: UTF-8
; 将狼才鲸于2022-09-29创建工程时，从Keil选择设备列表中的设备描述中拷贝过来的
; 芯片介绍：
; flexible configurable, single-clock 8051 compatible IP core with 
; 9.4 to 12.1 times more performance than legacy 80C51 
; (with Dhrystone v1.1 Benchmark on identical clock speed, 
; depending on the MDU, number of  DPTR and 
; auto inc/dec/switch implementation).
;
; Optional features: 
; 32 I/O lines, 
; three 16-bit timer/counters, 
; compare/capture unit (CCU), 
; 18 interrupts/4 priority levels or 6 interrupts/2 priority levels, 
; two serial interfaces (UARTs), 
; serial peripheral interface (SPI), 
; I2C interface, 
; secondary I2C interface, 
; 16-bit multiplication-division unit (MDU), 
; multiple DPTR with auto-increment/auto-switch support, 
; 15-bit programmable watchdog timer with configurable prescaler, 
; power management unit (PMU), 
; direct memory access (DMA) controller, 
; real time clock (RTC), 
; internally and externally generated wait states, 
; software reset, 
; program and data memory extension up to 8MB (banking). 
;
; Optionally available: On-Chip Debug Support for 
; Keil uVision Debugger.
;
; The R8051XC IP core can be implemented in FPGA and ASIC. 
;
; *** This device is configured for 8 DPTR registers ***
;------------------------------------------------------------------------------

;------------------------------------------------------------------------------
;  This file is part of the C51 Compiler package
;  Copyright (c) 1988-2005 Keil Elektronik GmbH and Keil Software, Inc.
;  Version 8.01
;
;  *** <<< Use Configuration Wizard in Context Menu >>> ***
;------------------------------------------------------------------------------
;  STARTUP.A51:  This code is executed after processor reset.
;
;  To translate this file use A51 with the following invocation:
;
;     A51 STARTUP.A51
;
;  To link the modified STARTUP.OBJ file to your application use the following
;  Lx51 invocation:
;
;     Lx51 your object file list, STARTUP.OBJ  controls
;
;------------------------------------------------------------------------------
;
;  User-defined <h> Power-On Initialization of Memory
;
;  With the following EQU statements the initialization of memory
;  at processor reset can be defined:
;
; <o> IDATALEN: IDATA memory size <0x0-0x100>
;     <i> Note: The absolute start-address of IDATA memory is always 0
;     <i>       The IDATA space overlaps physically the DATA and BIT areas.

; IDATA是8051 0x00~0x7F的128个iRAM区域
; 8051是8位CPU，数据总线8位，地址总线16位，地址范围0x0000~0xFFFF，但是地址分为0x0000~0x00FF和0x0100~0xFFFF两部分；
; 0x00~0xFF 256个地址属于iRAM，里面既有外设寄存器，也有CPU寄存器，也有通用寄存器；
; 0x0100~0xFFFF可以认为是64k 扩展RAM；
; 其中0x00~0x7F 128个是CPU通用的寄存器、可位寻址的寄存器，还有可作为堆栈区域的iRAM，这里称为iDATA；
; 另外0x80~0xFF 128个，属于SFR特殊功能寄存器，主要是外设寄存器和CPU寄存器。
IDATALEN        EQU     80H ; 只是一个宏定义，8051通用寄存器的个数，0x00~0x7F
;
; <o> XDATASTART: XDATA memory start address <0x0-0xFFFF> 
;     <i> The absolute start address of XDATA memory
XDATASTART      EQU     0   ; 64K扩展RAM的起始地址
;
; <o> XDATALEN: XDATA memory size <0x0-0xFFFF> 
;     <i> The length of XDATA memory in bytes.
XDATALEN        EQU     0   ; 64K扩展RAM的长度，最大64K，最小0
;
; <o> PDATASTART: PDATA memory start address <0x0-0xFFFF> 
;     <i> The absolute start address of PDATA memory
PDATASTART      EQU     0H  ; 外部64K扩展RAM中的低256字节起始地址，这部分地址操作比较特殊
;
; <o> PDATALEN: PDATA memory size <0x0-0xFF> 
;     <i> The length of PDATA memory in bytes.
PDATALEN        EQU     0H  ; 外部64K扩展RAM中的低256字节总长度
;
;</h>
;------------------------------------------------------------------------------
;
;<h> Reentrant Stack Initialization
;
;  The following EQU statements define the stack pointer for reentrant
;  functions and initialized it:
;
; <h> Stack Space for reentrant functions in the SMALL model.
;  <q> IBPSTACK: Enable SMALL model reentrant stack
;     <i> Stack space for reentrant functions in the SMALL model.
; 是否初始化堆栈，大概几十个字节
IBPSTACK        EQU     0       ; set to 1 if small reentrant is used.
;  <o> IBPSTACKTOP: End address of SMALL model stack <0x0-0xFF>
;     <i> Set the top of the stack to the highest location.
; 可重入堆栈（仿真栈）的最高地址，一般是0xFF，0x00~0x1F是四组R0~R7寄存器，0x20~0x2F是位寻址区域，0x30开始是堆栈的默认起始地址
IBPSTACKTOP     EQU     0xFF +1     ; default 0FFH+1  
; </h>
;
; <h> Stack Space for reentrant functions in the LARGE model.      
;  <q> XBPSTACK: Enable LARGE model reentrant stack
;     <i> Stack space for reentrant functions in the LARGE model.
XBPSTACK        EQU     0       ; set to 1 if large reentrant is used.
;  <o> XBPSTACKTOP: End address of LARGE model stack <0x0-0xFFFF>
;     <i> Set the top of the stack to the highest location.
; 扩展RAM中堆栈的栈顶在64K的位置
XBPSTACKTOP     EQU     0xFFFF +1   ; default 0FFFFH+1 
; </h>
;
; <h> Stack Space for reentrant functions in the COMPACT model.    
;  <q> PBPSTACK: Enable COMPACT model reentrant stack
;     <i> Stack space for reentrant functions in the COMPACT model.
PBPSTACK        EQU     0       ; set to 1 if compact reentrant is used.
;
;   <o> PBPSTACKTOP: End address of COMPACT model stack <0x0-0xFFFF>
;     <i> Set the top of the stack to the highest location.
; 扩展64K RAM中低256字节中作为堆栈，栈顶0xFF
PBPSTACKTOP     EQU     0xFF +1     ; default 0FFH+1  
; </h>
;</h>
;------------------------------------------------------------------------------
;
;  Memory Page for Using the Compact Model with 64 KByte xdata RAM
;  <e>Compact Model Page Definition
;
;  <i>Define the XDATA page used for PDATA variables. 
;  <i>PPAGE must conform with the PPAGE set in the linker invocation.
;
; Enable pdata memory page initalization
; 64扩展RAM需要进行分页读取256字节x256页
PPAGEENABLE     EQU     0       ; set to 1 if pdata object are used.
;
; <o> PPAGE number <0x0-0xFF> 
; <i> uppermost 256-byte address of the page used for PDATA variables.
PPAGE           EQU     0
;
; <o> SFR address which supplies uppermost address byte <0x0-0xFF> 
; <i> most 8051 variants use P2 as uppermost address byte
; 高地址字节占用P2 IO口锁存器地址0xA0，这应该是一种约定俗成的用法
PPAGE_SFR       DATA    0A0H
;
; </e>
;------------------------------------------------------------------------------

; Standard SFR Symbols 
; SFR特殊功能寄存器（芯片寄存器和外设寄存器）地址定义
ACC     DATA    0E0H
B       DATA    0F0H
SP      DATA    81H
DPL     DATA    82H
DPH     DATA    83H

                ; 声明本文件模块函数入口
                ; 这也是一个标量，这个标量会在别的地方真正定义
                NAME    ?C_STARTUP ; 芯片上电时运行的第一条语句所在处


?C_C51STARTUP   SEGMENT   CODE  ; 声明代码段名
?STACK          SEGMENT   IDATA ; 声明iRAM数据段名

                ;;以下是再定位内部RAM数据段
                RSEG    ?STACK  ; 用再定位段做当前段，以下的内容都放在这个数据段中
                DS      1       ; 从当前位置预留1字节空间备用

                EXTRN CODE (?C_START)   ; 声明外部有main函数入口
                PUBLIC  ?C_STARTUP      ; 输出本地的模块名标量

                ;;以下是绝对代码段
                CSEG    AT      0   ; 绝对代码段，地址从零开始
?C_STARTUP:     LJMP    STARTUP1    ; 本程序第一行运行的代码

                ;;以下是绝对代码段
                RSEG    ?C_C51STARTUP ; 用再定位段做当前段，以下内容放在当前端

STARTUP1:

; 清0x00~0x7F区域的寄存器
IF IDATALEN <> 0 ; 如果0x00~0x7F 127个通用寄存器数量不等于0，则进行以下处理
                MOV     R0,#IDATALEN - 1 ; 将最大的地址暂存到寄存器1
                CLR     A                ; 寄存器2赋值0（清寄存器）
IDATALOOP:      MOV     @R0,A            ; 将当前地址的寄存器赋值0
                DJNZ    R0,IDATALOOP     ; R0减1不为零则跳转到IDATALOOP标号继续执行；deduct减去，add加上
ENDIF

; 清64K所有的扩展RAM
IF XDATALEN <> 0
                MOV     DPTR,#XDATASTART    ; 将外部RAM起始地址0x00赋值给寄存器0；DPTR是16位的访问扩展RAM的地址
                MOV     R7,#LOW (XDATALEN)  ; 长度低字节赋值到寄存器0
  IF (LOW (XDATALEN)) <> 0  ; 如果低字节不为0
                MOV     R6,#(HIGH (XDATALEN)) +1 ; ; 进行256字节（BANK）对齐，只要一个BANK有数据，则清整个BANK
  ELSE                      ; 如果低字节为0
                MOV     R6,#HIGH (XDATALEN) ; 长度高字节赋值到寄存器1
  ENDIF
                CLR     A
XDATALOOP:      MOVX    @DPTR,A      ; 赋值一个外部RAM地址为0；低256字节需要用MOVX @R0
                INC     DPTR         ; 外部64K RAM地址自增1
                DJNZ    R7,XDATALOOP ; 双层循环的第一层循环
                DJNZ    R6,XDATALOOP ; 双层循环的第二层循环
ENDIF

; 定义好扩展64K RAM高地址值
IF PPAGEENABLE <> 0
                MOV     PPAGE_SFR,#PPAGE
ENDIF

; 清64K外部RAM中特殊的低256字节
IF PDATALEN <> 0
                MOV     R0,#LOW (PDATASTART)    ; 给变量0赋值特殊RAM起始地址，限定256以内
                MOV     R7,#LOW (PDATALEN)      ; 给变量1赋值特殊RAM长度，限定256以内
                CLR     A
PDATALOOP:      MOVX    @R0,A           ; 地址清零；地址自增；长度未循环完则一直循环
                INC     R0
                DJNZ    R7,PDATALOOP
ENDIF

; 如果需要，则定义各栈顶地址
; 8051数据存放的位置有几类，读写的速度也不一样，和32位CPU不一样：
; data：内部256字节iRAM，idata：64k扩展RAM的低256字节部分，
; pdata：R0 R1寻址的扩展64k RAM，xdata：DPTR寻址的扩展64k RAM
; 8051中分为硬件栈和仿真栈
IF IBPSTACK <> 0
EXTRN DATA (?C_IBP) ; 
                ; 仿真栈所在的iRAM地址栈顶，比如0xFF
                MOV     ?C_IBP,#LOW IBPSTACKTOP

ENDIF

; 如果需要，定义64K扩展RAM中的栈顶
IF XBPSTACK <> 0
EXTRN DATA (?C_XBP)

                MOV     ?C_XBP,#HIGH XBPSTACKTOP
                MOV     ?C_XBP+1,#LOW XBPSTACKTOP
ENDIF

; 如果需要，定义64K扩展RAM中的低256字节的栈顶
IF PBPSTACK <> 0
EXTRN DATA (?C_PBP) ; Keil内部函数指针，
                MOV     ?C_PBP,#LOW PBPSTACKTOP
ENDIF

                MOV     SP,#?STACK-1 ; iRAM数据段地址

; This code is required if you use L51_BANK.A51 with Banking Mode 4
;<h> Code Banking
; <q> Select Bank 0 for L51_BANK.A51 Mode 4
#if 0   
;     <i> Initialize bank mechanism to code bank 0 when using L51_BANK.A51 with Banking Mode 4.
EXTRN CODE (?B_SWITCH0) ; 初始化扩展RAM bank0
                CALL    ?B_SWITCH0      ; init bank mechanism to code bank 0
#endif
;</h>
                LJMP    ?C_START ; 跳转到main函数执行

                END     ; 汇编文件结束
